package se.webbzon.boltzmann.minigame.botany;

import java.awt.Color;
import java.awt.Image;
import java.util.ArrayList;
import java.util.Collection;

import se.webbzon.boltzmann.minigame.sandbox.SandBox;
import se.webbzon.boltzmann.minigame.sandbox.SandBoxObject;
import se.webbzon.oschi01.resourceset.ResourceSet;
import se.webbzon.oschi01.worldarea.WorldArea;
import se.webbzon.oschi01.worldcamera.WorldCameraPane;
import se.webbzon.oschi01.worldobject.WorldObject;

public abstract class AbstractCell extends SandBoxObject {

	// The 2-dimensional depth of the cell
	private static final int depth = 2;
	
	// The maximal size and relative oscillation of the cell
	private static final double maxSize = 64;
	private static final double relativeOscillation = 0.0625;
	
	// The cycle rates
	private static final double growRate = 0.002;
	private static final double idleRate = 0.003;
	private static final double splitRate = 0.006;
	private static final double dieRate = 0.004;
	
	// A collection of all the cell bars associated with this cell
	private final Collection<CellBar> bars;
	
	// The cycle of the cell
	private final CellBar cycle;
	
	// The current state of the cell
	private CellState state;
	
	// The oscillation properties of the cell
	private double t, dx, dy;
	
	/*============================================================
	Constructors
	============================================================*/

	public AbstractCell() {
		super(	WorldObject.onCreate + WorldObject.onDestroy + 
				WorldObject.onStep + WorldObject.onPostDraw, depth);
		bars = new ArrayList<CellBar>();
		cycle = new CellBar();
		cycle.setColor(Color.green);
		cycle.setValue(0.25);
		addBar(cycle);
		setSize(0,0);
		state = CellState.GROW;
		t = Math.PI * Math.random();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Kills this cell. **/
	public void die() {
		state = CellState.DIE;
	}
	
	/** Forces this cell to split. **/
	public void split() {
		state = CellState.SPLIT;
	}
	
	/** Adds a cell bar to this cell. **/
	public boolean addBar(CellBar bar) {
		return bars.add(bar);
	}
	
	/** Removes a cell bar to this cell. **/
	public boolean removeBar(CellBar bar) {
		return bars.remove(bar);
	}
	
	/** Returns the horizontal oscillation of the cell. **/
	public double getDeltaX() {
		return dx;
	}
	
	/** Returns the vertical oscillation of the cell. **/
	public double getDeltaY() {
		return dy;
	}
	
	@Override public void onAttach(SandBox box) {};

	@Override public void onDetach(SandBox box) {};
	
	@Override public void onCreate(WorldArea world) {
		
	}
	
	@Override public void onDestroy(WorldArea world) {
		
	}

	@Override public void onStep(WorldArea world) {		
		double p = live(cycle.getValue());
		if (p >= 1) {
			endCycle();
			p = 0;
		}
		cycle.setValue(p);
		
		t += Math.PI / 30;
		dx = relativeOscillation * getWidth() * Math.cos(t);
		dy = relativeOscillation * getHeight() * Math.sin(t);
		if (t >= Math.PI*2)
			t -= Math.PI*2;
	}

	@Override public void onPostDraw(WorldArea world, WorldCameraPane hud) {
		double dx = getDeltaX(), dy = getDeltaY();
		int x = (int) getX();
		int y = (int) getY();
		int wHalf = (int) (getWidth() / 2 + dx);
		int hHalf = (int) (getHeight() / 2 + dy);
		int w = 2 * wHalf;
		hud.drawImage(getImage().get(0), x - wHalf, y - hHalf, w, 2*hHalf);
		
		y += hHalf + 4;
		for (CellBar bar : bars) {
			hud.setColor(Color.darkGray);
			hud.fillRect(x - 32, y, 64, 4);
			hud.setColor(bar.getColor());
			int bw = (int) ((double) 64 * bar.getValue());
			hud.fillRect(x - 32, y, bw, 4);
			hud.setColor(Color.black);
			hud.drawRect(x - 32, y, 64, 4);
			y += 6;
		}
	}
	
	/*============================================================
	Protected Methods
	============================================================*/
	
	/** Returns the resource set containing the image of the cell. **/
	protected abstract ResourceSet<Image> getImage();
	
	/** Returns true if this cell is healthy enough to live on. **/
	protected abstract boolean isHealthy();
	
	/** Returns true if this cell is healthy enough to reproduce. **/
	protected abstract boolean canSplit();
	
	/** Returns true if this cell is healthy enough to react. **/
	protected abstract boolean canReact();
	
	/** Performs an idle cell consumption **/
	protected abstract void onConsume();
	
	/** Performs a cell reaction. **/
	protected abstract void onReact();
	
	/** Performs a cell split. **/
	protected abstract void onSplit();
	
	/*============================================================
	Private Methods
	============================================================*/
	
	private final double live(double p) {
		switch (state) {
			case GROW:
				setSize(p * maxSize, p * maxSize);
				return p + growRate;
			case IDLE:
				onConsume();
				if (!isHealthy()) {
					die();
					return 0;
				} else
					return p + idleRate;
			case SPLIT:
				setSize((1 + 1.5 * p) * maxSize, (1 - 0.5 * p) * maxSize);
				return p + splitRate;
			default:
				setSize((1 - p) * maxSize, (1 - p) * maxSize);
				return p + dieRate;
		}
	}
	
	private final void endCycle() {
		switch (state) {
			case GROW:
				state = CellState.IDLE;
				break;
			case IDLE:
				if (canSplit())
					split();
				else if (canReact()) {
					state = CellState.GROW;
					cycle.setValue(0.75);
					onReact();
				}
				break;
			case SPLIT:
				onSplit();
				die();
				break;
		}
	}

}
